home *** CD-ROM | disk | FTP | other *** search
- /*============================================================
- FailureHander.c
-
- greggor@apple.com
-
- The API of this package is similar to the one used
- internally by the System 7 Finder. The implementation
- is different, though.
-
- Usage:
-
- #include "FailureHandler.h"
-
- ptr1 = nil;
- ptr2 = nil;
-
- TRY
- {
- ptr1 = NewPtr( 1024 * 1024 );
- FailMemError();
- ptr2 = NewPtr( 15 );
- FailMemError();
-
- FailOSErr( SomeFnReturningOSErr() );
- }
- EXCEPT
- {
- // Clean up here
-
- if( ptr1 != nil )
- DisposPtr( ptr1 );
- if( ptr2 != nil )
- DisposPtr( ptr2 );
- }
- ENDTRY
-
- ============================================================*/
- #include <Types.h>
- #include <Memory.h>
- #include <Resources.h>
- #include <setjmp.h>
-
- #include "FailureHandler.h"
-
- #define DEBUGMESSAGES
- /*
- #define FAILMESSAGES
- */
-
- #define kResignalingFailure 0x10
-
- FailStack* gFailStack = nil;
- NotifyFailureProc gNotifyProc = nil;
-
- /*-------------------------------------------------------------------
- This function is called by the TRY macro
- -------------------------------------------------------------------*/
- void SetupTry( FailStack* newFailHandler )
- {
- newFailHandler->magicFailID = kMagicFailID;
- /*
- // Clear out the 'error' & 'flags' entries
- */
- newFailHandler->error = noErr;
- newFailHandler->flags = 0;
- /*
- // Push the new entry onto the failure stack
- */
- newFailHandler->next = gFailStack;
- gFailStack = newFailHandler;
- }
-
- /*-------------------------------------------------------------------
- 'PopFailureStack' is called by the ENDTRY macro
- -------------------------------------------------------------------*/
- void PopFailureStack()
- {
- if( (gFailStack != nil) && (gFailStack->magicFailID == kMagicFailID) )
- {
- /*
- // Pop an entry off the top of the fail stack
- */
- gFailStack = gFailStack->next;
- }
- #ifdef DEBUGMESSAGES
- else
- {
- if( gFailStack == nil )
- DebugStr( "\pError -- ENDTRY without TRY" );
- else
- DebugStr( "\pFatal error in PopFailureStack--failure handler stack corrupted" );
- }
- #endif
- }
-
- /*-------------------------------------------------------------------
- Invoke exception handling if 'theErr' != noErr
- -------------------------------------------------------------------*/
- void FailOSErr( OSErr theErr )
- {
- if( theErr != noErr )
- Failure( theErr );
- }
-
- /*-------------------------------------------------------------------
- Invoke exception handling
- -------------------------------------------------------------------*/
- void Failure( OSErr theErr )
- {
- #ifdef FAILMESSAGES
- DebugPrintf( "Failure caused by an error %d", theErr );
- #endif
- /*
- // First do a little bit of sanity checking
- */
- if( (gFailStack != nil) && (gFailStack->magicFailID == kMagicFailID) )
- {
- /*
- // If we are already in a failure handler, call
- // 'FailAgain'; don't go back to the same handler!
- */
- if( gFailStack->flags & kInExceptionHandler )
- FailAgain();
- /*
- // Remember the error that caused the failure &
- // set a bit that indicates that we have invoked
- // exception handling
- */
- gFailStack->error = theErr;
- gFailStack->flags |= kInExceptionHandler;
- /*
- // Record the stack chain in case someone is
- // interested in it later. Don't blast the
- // old stack chain if we are resignalling, though!
- */
- if( (gNotifyProc != nil) && ((gFailStack->flags & kResignalingFailure) == 0) )
- (*gNotifyProc)();
-
- /*
- // TRY does a setjmp; now we longjmp back to
- // that point with a longjmp. The 'TRY' macro
- // includes an 'if' statement that is true
- // the first time through and false when we longjmp
- // back to it; the EXCEPTion handler is just the
- // 'else' branch of that 'if' block.
- */
- longjmp( gFailStack->env, 1 );
- }
- #ifdef DEBUGMESSAGES
- else
- {
- if( gFailStack == nil )
- DebugStr( "\pFatal error in FailOSErr--gFailStack nil" );
- else
- DebugStr( "\pFatal error in FailOSErr--failure handler stack corrupted" );
- }
- #endif
- }
-
- /*-------------------------------------------------------------------
- Return the error code that caused the failure.
-
- It is only legal to request TheFailError within an EXCEPT block
- -------------------------------------------------------------------*/
- OSErr TheFailError()
- {
- if( gFailStack == nil )
- return ePointerNil;
-
- #ifdef DEBUGMESSAGES
- if( gFailStack->magicFailID != kMagicFailID )
- DebugStr( "\pFatal error in TheFailError--failure handler stack corrupted" );
- #endif
-
- return gFailStack->error;
- }
-
- /*-------------------------------------------------------------------
- It is only legal to call 'FailAgain' from within an EXCEPT
- block. If called, FailAgain will pass the error that
- caused the exception failure on to the failure handler
- above this one.
- -------------------------------------------------------------------*/
- void FailAgain()
- {
- OSErr theErr;
-
- /*
- // Check for programming errors
- */
- if( (gFailStack != nil) && (gFailStack->next != nil) )
- {
- /*
- // Get the error; make sure it is not 'noErr'
- */
- theErr = TheFailError();
- if( theErr == noErr )
- theErr = eGeneralErr;
- /*
- // Pop off this failure handler and fail again
- */
- PopFailureStack();
- gFailStack->flags |= kResignalingFailure;
- Failure( theErr );
- }
- #ifdef DEBUGMESSAGES
- else
- {
- DebugStr( "\pBad call to FailAgain" );
- }
- #endif
- }
-
- /*-------------------------------------------------------------------
- Install a single routine called at failure time before
- exception processing begins. This function is used in
- conjunction with ShowStackChain to record and later show
- the stack at the time of failure.
- -------------------------------------------------------------------*/
- void SetExceptionNotifyProc(NotifyFailureProc notifyProc)
- {
- gNotifyProc = notifyProc;
- }
-
- /*-------------------------------------------------------------------
- This routine is used by ReportError so that it knows whether or
- not it should show the 'sc6' button
- -------------------------------------------------------------------*/
- Boolean ExceptionNotifyProcInstalled(void)
- {
- return gNotifyProc != nil;
- }
-
- /*-------------------------------------------------------------------
- This routine doesn't actually do anything; it's just used
- by the NOREGISTER macro
- -------------------------------------------------------------------*/
- void MakeVariableNoRegister( void* foo )
- {
-
- }
-